[6.x] Only preload meta for the selected link type - #15113
Conversation
`Link::preload()` built the full nested relationship meta for every registered link type on every instance of the field. In a nested Replicator with hundreds of link fields, that's hundreds of byte-identical copies of the same config derived payload. The rest are now fetched from `fields/field-meta` the first time the user picks that type. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jasonvarga
left a comment
There was a problem hiding this comment.
Requires changes — found a regression while reviewing this PR, reproduced live in a sandbox.
Issue
LinkFieldtype.vue now gates rendering the nested fieldtype component on matchedType.meta being truthy:
<component v-else-if="matchedType && matchedType.meta" ... />
<Icon v-else-if="matchedType" name="loading" class="size-4 self-center" />And Link::preload() (src/Fieldtypes/Link.php) only preloads meta for the initial option now; every other type gets meta: null.
The problem: null is also exactly what a legitimately-loaded meta looks like for any fieldtype that doesn't override Fieldtype::preload() — the base implementation just returns null (src/Fields/Fieldtype.php:374-377). So any custom Link::extend() type built on a plain fieldtype (e.g. text, toggle, integer) will have meta === null forever — both from the initial preload and from every subsequent fields/field-meta fetch, since that endpoint just calls preload() again and gets the same null back.
Since the component only renders once meta is truthy, the field gets stuck on the loading spinner permanently and becomes completely unusable, with no error shown to the user. This isn't hypothetical — it's exactly the shape of this repo's own TestBasicLinkType fixture in tests/Fieldtypes/LinkTest.php (fieldtype() => ['type' => 'text']). I reproduced it live: registered a custom email Link type backed by the text fieldtype, added it to a blueprint, selected it in the CP — permanent spinner, field never renders.
Suggested fix
Track fetch status explicitly instead of inferring it from the value — add a metaLoaded boolean alongside meta:
src/Fieldtypes/Link.php:
$types[$handle] = [
...
'meta' => $handle === $initialOption ? $nestedFieldtype->preload() : null,
'metaLoaded' => $handle === $initialOption,
'selected' => $selected ? [$selected] : [],
];LinkFieldtype.vue:
<component v-else-if="matchedType && matchedType.metaLoaded" ... />
<Icon v-else-if="matchedType" name="loading" ... />loadTypeMeta(handle) {
const type = this.meta.types[handle];
if (!type || type.metaLoaded) return Promise.resolve();
...
},
updateTypeMeta(handle, typeMeta) {
this.updateMeta({
...this.meta,
types: { ...this.meta.types, [handle]: { ...this.meta.types[handle], meta: typeMeta, metaLoaded: true } },
});
},Everything else here looks solid — the built-in Entry/Asset path (the actual reported performance issue) is correctly fixed and well tested. This is scoped to custom Link types built on non-preloadable fieldtypes.
(Comment posted via Claude)
`null` is also what a legitimately loaded meta looks like for any fieldtype that doesn't override `Fieldtype::preload()`, so custom link types built on plain fieldtypes (like `text`) were stuck on the loading spinner forever. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jasonvarga
left a comment
There was a problem hiding this comment.
Confirmed fixed in 168b130 — the metaLoaded flag correctly separates "not fetched yet" from "legitimately null," so custom Link types on non-preloadable fieldtypes (e.g. text) no longer get stuck loading. Verified:
- Read the diff line-by-line, matches the suggested fix.
it_marks_meta_as_loaded_for_a_type_whose_fieldtype_preloads_nothing(PHP) anddoes not request meta again for a type whose fieldtype has no meta to preload(JS) directly cover the scenario I reproduced../vendor/bin/phpunit tests/Fieldtypes/LinkTest.phppasses locally (35/35).- CI green.
Approving.
(Comment posted via Claude)
This pull request fixes an issue where entry edit pages containing lots of Link fields inside nested Replicators would take a long time to load, and often end in a blank page or a timeout.
This was happening because
Link::preload()built the full nested relationship meta for every registered link type, on every instance of the field, whether or not that type was actually selected. That meta is derived entirely from the field's config, so a page with hundreds of link fields shipped hundreds of byte-identical copies of it. In the reported case, 1,008 link fields produced 1,008 identical copies of the entries meta, accounting for 97% of an 11mbdata-pagepayload.This PR fixes it by only preloading meta for the type matching the field's initial option. The rest send
null, and the Control Panel fetches them from the existingfields/field-metaendpoint the first time the user picks that type. On a test entry with 480 cells (960 link fields), the payload drops from 9.5mb to 1.9mb.Fixes #14664